import {readTextFile} from "@/util/file";
import {XMLBuilder, XMLParser} from "fast-xml-parser";

class XmlNode {
    name = ''
    value = null
    attributes = {}
    children = []

    /**
     * 创建一个 XmlNode 实例
     * @param {string} name - 节点名称，必须为字符串且不能为空
     * @param {string} value - 节点值，可以为字符串或 null
     * @param {Record<string, string>} attributes - 节点属性对象，键值均为字符串
     * @param {XmlNode[]} children - 子节点数组，每个元素必须是 XmlNode 实例
     */
    constructor({name = '', value = null, attributes = {}, children = []}) {
        this.name = name;
        this.value = value;
        this.attributes = attributes;
        this.children = children;
    }

    /**
     * 从 fast-xml-parser 解析出的对象构建 XmlNode 实例
     *
     * 此方法用于将由 fast-xml-parser 解析 XML 后生成的 JavaScript 对象
     * 转换为 XmlNode 对象，以便进一步操作和访问 XML 节点内容。
     *
     * @param {Object} obj - 由 fast-xml-parser 解析生成的 XML 节点对象
     * @returns {XmlNode} 返回一个 XmlNode 实例
     * @throws {Error} 如果输入为 null、undefined 或不是对象时抛出错误
     */
    static parseFromFastXmlParserObj(obj) {
        if (!obj) {
            throw new Error('obj is null')
        }
        if (Object.prototype.toString.call(obj) === '[object Object]') {
            // 定义 fast-xml-parser 中用于表示属性和文本值的特殊键
            const ATTRIBUTE_KEY = ':@'
            const VALUE_KEY = '#text'

            // 获取当前节点的名称（排除属性键）
            const name = Object.keys(obj).filter(key => key !== ATTRIBUTE_KEY)[0]

            // 提取当前节点的属性
            const attributes = obj[ATTRIBUTE_KEY]

            // 用于存储节点的文本值和子节点
            let value = null
            const children = []

            // 处理子节点或文本值
            if (Object.prototype.toString.call(obj[name]) === '[object Array]') {
                // 当子节点为数组时，判断是否为纯文本节点，否则递归解析子节点
                if (obj[name].length === 1 && obj[name][0][VALUE_KEY] !== null && obj[name][0][VALUE_KEY] !== undefined) {
                    value = obj[name][0][VALUE_KEY]
                } else {
                    for (let i = 0; i < obj[name].length; i++) {
                        children.push(XmlNode.parseFromFastXmlParserObj(obj[name][i]))
                    }
                }
            } else if (Object.prototype.toString.call(obj[name]) === '[object Object]') {
                // 当子节点为对象时，判断是否为纯文本节点，否则递归解析该子节点
                if (obj[name][VALUE_KEY]) {
                    value = obj[name][VALUE_KEY]
                } else {
                    children.push(XmlNode.parseFromFastXmlParserObj(obj[name]))
                }
            }

            // 创建并返回新的 XmlNode 实例
            return new XmlNode({
                name,
                value,
                attributes,
                children
            })

        }
        throw new Error('obj is not object')
    }

    /**
     * 将当前对象转换为适配 fast-xml-parser 的对象结构
     *
     * 该方法主要用于将自定义对象结构转换为 fast-xml-parser 所需的格式，
     * 以便后续可以通过 XMLBuilder 正确地将其序列化为 XML 字符串。
     *
     * @returns {Object} 转换后的对象，符合 fast-xml-parser 的结构要求
     */
    toFastXmlParserObj() {
        const obj = {}

        // 将属性对象中的每个键值对转换为 @_key 的形式，以适配 fast-xml-parser 的属性格式
        let keys = Object.keys(this.attributes);
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i]
            obj['@_' + key] = this.attributes[key]
        }

        // 如果当前节点有子节点，则递归处理每个子节点
        if (this.children.length) {
            for (let i = 0; i < this.children.length; i++) {
                const node = this.children[i]
                if (obj[node.name]) {
                    obj[node.name].push(node.toFastXmlParserObj())
                } else {
                    obj[node.name] = [node.toFastXmlParserObj()]
                }
            }
        } else if (this.value !== null && this.value !== '') {
            // 如果没有子节点但有值，则直接返回该值作为节点内容
            return this.value
        }

        return obj
    }
}

class MavenPomXml {

    header = '<?xml version="1.0" encoding="UTF-8"?>'

    root = new XmlNode({})

    parent = null

    /**
     * 从 fast-xml-parser 解析出的对象构建 MavenPomXml 实例
     *
     * 此方法用于处理 fast-xml-parser 解析 XML 后生成的 JavaScript 对象，
     * 将其转换为 MavenPomXml 对象，以便进一步操作和访问 POM 文件内容。
     *
     * @param {Object|Array} obj - 由 fast-xml-parser 解析生成的对象或数组
     * @returns {MavenPomXml} 返回一个 MavenPomXml 实例
     * @throws {Error} 如果输入为 null、undefined 或既不是对象也不是数组时抛出错误
     */
    static parseFromFastXmlParserObj(obj) {
        if (!obj) {
            throw new Error('obj is null')
        }
        // 处理数组类型输入，取数组最后一个元素解析为 XmlNode
        if (Object.prototype.toString.call(obj) === '[object Array]') {
            return new MavenPomXml(XmlNode.parseFromFastXmlParserObj(obj[obj.length - 1]))
        } else if (Object.prototype.toString.call(obj) === '[object Object]') {
            // 处理对象类型输入，直接解析为 XmlNode
            return new MavenPomXml(XmlNode.parseFromFastXmlParserObj(obj))
        }
        throw new Error('obj is not object or array')
    }

    /**
     * 创建一个 MavenPomXml 实例
     * @param root {XmlNode}
     */
    constructor(root) {
        this.root = root;
    }

    /**
     * 获取父项目的groupId
     *
     * @returns {string|null} 父项目的groupId，如果不存在父项目则返回null
     */
    get parentGroupId() {
        const parentNode = this.root.children.filter(item => item.name === 'parent')[0]
        if (parentNode) {
            return parentNode.children.filter(item => item.name === 'groupId')[0].value
        } else {
            return null
        }
    }

    /**
     * 设置父项目的groupId
     *
     * @param {string} _parentGroupId - 新的父项目groupId值
     */
    set parentGroupId(_parentGroupId) {
        const parentNode = this.root.children.filter(item => item.name === 'parent')[0]
        if (parentNode) {
            parentNode.children.filter(item => item.name === 'groupId')[0].value = _parentGroupId
        }
    }

    /**
     * 获取父项目的artifactId
     *
     * @returns {string|null} 父项目的artifactId，如果不存在父项目则返回null
     */
    get parentArtifactId() {
        const parentNode = this.root.children.filter(item => item.name === 'parent')[0]
        if (parentNode) {
            return parentNode.children.filter(item => item.name === 'artifactId')[0].value
        } else {
            return null
        }
    }

    /**
     * 设置父项目的artifactId
     *
     * @param {string} _parentArtifactId - 新的父项目artifactId值
     */
    set parentArtifactId(_parentArtifactId) {
        const parentNode = this.root.children.filter(item => item.name === 'parent')[0]
        if (parentNode) {
            parentNode.children.filter(item => item.name === 'artifactId')[0].value = _parentArtifactId
        }
    }

    /**
     * 获取父项目的版本号
     *
     * @returns {string|null} 父项目的版本号，如果不存在父项目则返回null
     */
    get parentVersion() {
        const parentNode = this.root.children.filter(item => item.name === 'parent')[0]
        if (parentNode) {
            return parentNode.children.filter(item => item.name === 'version')[0].value
        } else {
            return null
        }
    }

    /**
     * 设置父项目的版本号
     *
     * @param {string} _parentVersion - 新的父项目版本号
     */
    set parentVersion(_parentVersion) {
        const parentNode = this.root.children.filter(item => item.name === 'parent')[0]
        if (parentNode) {
            parentNode.children.filter(item => item.name === 'version')[0].value = _parentVersion
        }
    }

    /**
     * 获取项目的groupId
     *
     * @returns {string|null} 项目的groupId，如果项目未设置groupId且无父项目则返回null
     */
    get groupId() {
        const groupIdNode = this.root.children.filter(item => item.name === 'groupId')[0]
        if (groupIdNode) {
            return groupIdNode.value
        } else {
            return this.parentGroupId
        }
    }

    /**
     * 设置项目的groupId
     *
     * @param {string} _groupId - 新的groupId值
     */
    set groupId(_groupId) {
        // 如果artifactId包含连字符，更新dependencies中的groupId
        if (this.artifactId.indexOf('-') > -1) {
            const list = this.root.children.filter(item => item.name === 'dependencies')
            if (list.length > 0) {
                list[0]
                    .children.filter(item => item.children.filter(item1 => item1.name === 'groupId')[0].value === this.groupId)
                    .forEach(item => {
                        item.children.filter(item1 => item1.name === 'groupId')[0].value = _groupId
                    })
            }
        } else {
            // 如果artifactId不包含连字符，更新dependencyManagement中的groupId
            const list = this.root.children.filter(item => item.name === 'dependencyManagement')
            if (list.length > 0) {
                list[0].children[0]
                    .children.filter(item => item.children.filter(item1 => item1.name === 'groupId')[0].value === this.groupId)
                    .forEach(item => {
                        item.children.filter(item1 => item1.name === 'groupId')[0].value = _groupId
                    })
            }
        }

        // 更新根节点的groupId
        const groupIdNode = this.root.children.filter(item => item.name === 'groupId')[0]
        if (groupIdNode) {
            groupIdNode.value = _groupId
        }
        this.parentGroupId = _groupId
    }

    /**
     * 获取项目的artifactId
     *
     * @returns {string} 项目的artifactId
     */
    get artifactId() {
        return this.root.children.filter(item => item.name === 'artifactId')[0].value
    }

    /**
     * 设置项目的artifactId
     *
     * @param {string} _artifactId - 新的artifactId值
     */
    set artifactId(_artifactId) {

        if (this.artifactId.indexOf('-') > -1) {

            // 如果原artifactId包含连字符，提取第一个连字符前的部分作为父artifactId
            const oldParentArtifactId = _artifactId.split('-')[0]

            // 更新依赖项中与当前groupId匹配的artifactId
            let list = this.root.children.filter(item => item.name === 'dependencies')
            if (list.length > 0) {
                list[0]
                    .children.filter(item => item.children.filter(item1 => item1.name === 'groupId')[0].value === this.groupId)
                    .forEach(item => {
                        const oldArtifactId = item.children.filter(item1 => item1.name === 'artifactId')[0].value
                        item.children.filter(item1 => item1.name === 'artifactId')[0].value = oldArtifactId.replace(this.parentArtifactId, oldParentArtifactId)
                    })
            }

            this.parentArtifactId = oldParentArtifactId
        } else {
            // 更新properties中的版本属性名
            let list = this.root.children.filter(item => item.name === 'properties')
            if (list.length > 0) {
                list = list[0].children.filter(item => item.name === this.artifactId + '.version')
                if (list.length > 0) {
                    list[0].name = _artifactId + '.version'
                }
            }

            // 更新依赖管理中与当前groupId匹配的artifactId和版本引用
            list = this.root.children.filter(item => item.name === 'dependencyManagement')
            if (list.length > 0) {
                list[0].children[0]
                    .children.filter(item => item.children.filter(item1 => item1.name === 'groupId')[0].value === this.groupId)
                    .forEach(item => {
                        const oldArtifactId = item.children.filter(item1 => item1.name === 'artifactId')[0].value
                        item.children.filter(item1 => item1.name === 'artifactId')[0].value = oldArtifactId.replace(this.artifactId, _artifactId)

                        item.children.filter(item1 => item1.name === 'version')[0].value = '${' + _artifactId + '.version}'
                    })
            }

            // 更新模块中的artifactId引用
            list = this.root.children.filter(item => item.name === 'modules')
            if (list.length > 0) {
                list[0].children.forEach(item => item.value = item.value.replace(this.artifactId, _artifactId))
            }
        }

        // 设置项目根节点下的artifactId元素值
        this.root.children.filter(item => item.name === 'artifactId')[0].value = _artifactId
    }

    /**
     * 获取项目的版本号
     *
     * @returns {string|null} 项目的版本号，如果项目未设置版本号且无父项目则返回null
     */
    get version() {
        const versionNode = this.root.children.filter(item => item.name === 'version')[0]
        if (versionNode) {
            return versionNode.value
        } else {
            return this.parentVersion
        }
    }

    /**
     * 设置项目的版本号
     *
     * @param {string} _version - 新的版本号
     */
    set version(_version) {
        const versionNode = this.root.children.filter(item => item.name === 'version')[0]
        if (versionNode) {
            versionNode.value = _version
        }
        this.parentVersion = _version

        if (this.artifactId.indexOf('-') === -1) {
            let list = this.root.children.filter(item => item.name === 'properties')
            if (list.length > 0) {
                list = list[0].children.filter(item => item.name === this.artifactId + '.version')
                if (list.length > 0) {
                    list[0].value = _version
                }
            }
        }
    }

    /**
     * 获取项目的模块列表
     *
     * @returns {Array<string>|null} 项目模块名称数组，如果不存在模块则返回null
     */
    get modules() {
        const modulesNode = this.root.children.filter(item => item.name === 'modules')[0]
        if (modulesNode) {
            return modulesNode.children.map(item => item.value)
        }
        return null
    }

    /**
     * 将当前对象转换为格式化后的 XML 字符串
     *
     * 该方法使用 fast-xml-parser 的 XMLBuilder 将当前对象结构转换为 XML 字符串。
     * 包含 XML 头部信息，并对输出进行格式化和空节点过滤。
     *
     * @returns {string} 格式化后的 XML 字符串
     */
    toXml() {
        const xmlText = this.header + '\n'
        const xmlBuilder = new XMLBuilder({
            ignoreAttributes: false,
            format: true,
            indentBy: '    ',
            suppressEmptyNode: true,
            attributeNamePrefix: '@_'
        });

        return xmlText + xmlBuilder.build(this.toFastXmlParserObj())
    }

    /**
     * 将当前对象转换为适配 fast-xml-parser 的结构
     *
     * 该方法将当前对象的根节点转换为符合 fast-xml-parser 要求的对象结构。
     * 返回的对象以根节点名称作为键，值为递归转换后的子节点结构。
     *
     * @returns {Object} 适配 fast-xml-parser 的对象结构
     */
    toFastXmlParserObj() {
        return {
            [this.root.name]: this.root.toFastXmlParserObj()
        }
    }
}

class JavenMavenPom {
    parent = null
    pom = null
    children = null
    path = ''

    constructor({pom, path, parent, children}) {
        this.pom = pom;
        this.path = path;
        this.parent = parent;
        this.children = children;
    }

    getAllJavaPom() {
        const list = [this]
        if (this.children) {
            for (const child of this.children) {
                list.push(...child.getAllJavaPom())
            }
        }
        return list
    }

    getAllPom() {
        return this.getAllJavaPom().map(item => item.pom)
    }
}

/**
 * 获取pom.xml文件的内容
 *
 * @param {string} path - pom.xml文件的路径
 * @returns {Promise<string>} 返回一个Promise，resolve时得到文件内容文本
 * @throws {Error} 当选择的文件不是pom.xml时抛出错误
 */
export async function getPomText(path) {
    if (!path.endsWith('pom.xml')) {
        throw new Error('请选择pom.xml文件');
    }

    return await readTextFile(path);
}

/**
 * 异步读取并解析指定路径下的 pom.xml 文件内容
 *
 * 该函数首先验证文件路径是否以 'pom.xml' 结尾，
 * 然后读取文件内容并使用 XMLParser 解析为对象结构，
 * 最终通过 MavenPomXml 的静态方法转换为结构化对象。
 *
 * @param {string} path - 要读取的 pom.xml 文件路径
 * @returns {Promise<MavenPomXml>} 解析后的 MavenPomXml 结构化对象
 * @throws {Error} 如果文件路径无效或解析失败
 */
export async function getPomInfo(path) {
    const pomText = await getPomText(path);

    const parser = new XMLParser({
        ignoreAttributes: false, // 保留属性信息
        preserveOrder: true,     // 保留节点顺序
        attributeNamePrefix: '', // 不使用属性前缀，属性名直接作为键
    });

    try {
        return MavenPomXml.parseFromFastXmlParserObj(parser.parse(pomText));
    } catch (err) {
        console.error('解析pom.xml失败:', err);
        throw err;
    }
}

/**
 * 异步读取并构建多个 pom.xml 文件的结构化对象，并使用 BFS 构建完整的父子结构树
 *
 * 该函数从传入的文件列表中筛选出所有 pom.xml 文件，
 * 依次解析每个 pom.xml 文件内容并构造成 JavenMavenPom 实例，
 * 建立 artifactId 映射表，并使用广度优先算法构建完整的父子项目结构树。
 *
 * @param {Array<{name: string, path: string}>} fileList - 文件列表，每个元素包含文件名和路径
 * @returns {Promise<Object>} 构建完成的完整 Maven 项目结构对象（树状结构）
 */
export async function getPomObj(fileList) {
    const pathList = fileList
        .filter(item => item.name === 'pom.xml')
        .map(item => item.path);

    const pomObjList = [];
    for (const path of pathList) {
        const pomInfo = await getPomInfo(path);
        pomObjList.push(new JavenMavenPom({
            pom: pomInfo,
            path: path,
        }));
    }

    const map = {};
    for (const pomObj of pomObjList) {
        map[pomObj.pom.artifactId] = pomObj;
    }

    const root = pomObjList.find(item => item.pom.parentArtifactId === null);

    if (!root) {
        throw new Error('未找到根项目（无父项目的项目）');
    }

    // 初始化队列
    const queue = [{node: root, parent: null}];

    while (queue.length > 0) {
        const {node, parent} = queue.shift();

        // 设置父节点
        if (parent) {
            node.parent = parent;
        }

        // 构建子节点
        if (node.pom.modules && node.pom.modules.length > 0) {
            node.children = [];

            for (const moduleName of node.pom.modules) {
                const childNode = map[moduleName];
                if (!childNode) {
                    console.warn(`模块 ${moduleName} 未找到对应的 pom.xml`);
                    continue;
                }

                node.children.push(childNode);
                queue.push({node: childNode, parent: node});
            }
        }
    }

    return root;
}
